#include "nm_file.h"

kfile::kfile()
{
	m_filehandle = 0;
	m_fileobj = NULL;
}

void kfile::Release()
{
	Close();
}

VOID kfile::Close()
{
	if (m_fileobj != NULL) {

		ObDereferenceObject(m_fileobj);
		m_fileobj = NULL;
	}

	if (m_filehandle != NULL) {

		ZwClose(m_filehandle);
		m_filehandle = 0;
	}
}

NTSTATUS kfile::Create( PWCHAR FileName, ULONG CreateDisposition /*= FILE_OPEN*/, ACCESS_MASK DesiredAccess /*= GENERIC_READ*/, ULONG ShareAccess /*= FILE_SHARE_READ */ )
{
	UNICODE_STRING Name;

	RtlInitUnicodeString(&Name, FileName);

	return Create(&Name, CreateDisposition, DesiredAccess, ShareAccess);
}

NTSTATUS kfile::Create( PUNICODE_STRING FileName, ULONG CreateDisposition /*= FILE_OPEN*/, ACCESS_MASK DesiredAccess /*= GENERIC_READ*/, ULONG ShareAccess /*= FILE_SHARE_READ */ )
{
	NTSTATUS ns;
	OBJECT_ATTRIBUTES oa;
	IO_STATUS_BLOCK Iob;

	Close();

	InitializeObjectAttributes(&oa, FileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, NULL);

	ns = IoCreateFile(&m_filehandle, 
		DesiredAccess | SYNCHRONIZE, 
		&oa, 
		&Iob, 
		NULL, 
		FILE_ATTRIBUTE_NORMAL, 
		ShareAccess, 
		CreateDisposition, 
		FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, 
		NULL, 
		0, 
		CreateFileTypeNone, 
		NULL, 
		IO_NO_PARAMETER_CHECKING);

	if (!NT_SUCCESS(ns)) {

		return ns;
	}

	ns = ObReferenceObjectByHandle(m_filehandle, 0, *IoFileObjectType, KernelMode, (PVOID *)&m_fileobj, NULL);
	if (!NT_SUCCESS(ns)) {

		ZwClose(m_filehandle);
		m_filehandle = 0;
	}

	return ns;
}

NTSTATUS kfile::Create( kstringw& FileName, ULONG CreateDisposition /*= FILE_OPEN*/, ACCESS_MASK DesiredAccess /*= GENERIC_READ*/, ULONG ShareAccess /*= FILE_SHARE_READ */ )
{
	return Create((PUNICODE_STRING)FileName, CreateDisposition, DesiredAccess, ShareAccess);
}

NTSTATUS kfile::Delete( BOOLEAN Del /*= TRUE*/ )
{
	NTSTATUS ns;
	IO_STATUS_BLOCK Iob;
	FILE_DISPOSITION_INFORMATION FileDispInfo;

	FileDispInfo.DeleteFile = Del;

	ns = ZwSetInformationFile(m_filehandle,
		&Iob,
		&FileDispInfo,
		sizeof(FileDispInfo),
		FileDispositionInformation);

	return ns;
}

NTSTATUS kfile::Flush()
{
	IO_STATUS_BLOCK Iob;
	PDEVICE_OBJECT deviceObject;
	PIRP irp;
	PIO_STACK_LOCATION irpSp;

	deviceObject = IoGetRelatedDeviceObject( m_fileobj );

	//
	// Allocate and initialize the I/O Request Packet (IRP) for this operation.
	//

	irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
	if (!irp) {

		return STATUS_INSUFFICIENT_RESOURCES;
	}

	irp->Tail.Overlay.OriginalFileObject = m_fileobj;
	irp->Tail.Overlay.Thread = PsGetCurrentThread();
	irp->RequestorMode = KernelMode;
	irp->UserEvent = (PKEVENT) NULL;
	irp->UserIosb = &Iob;
	irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;

	//
	// Get a pointer to the stack location for the first driver.  This is used
	// to pass the original function codes and parameters.
	//

	irpSp = IoGetNextIrpStackLocation( irp );
	irpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS;
	irpSp->FileObject = m_fileobj;

	return IoCallDriver(deviceObject, irp);
}

HANDLE kfile::GetHandle()
{
	return m_filehandle;
}

NTSTATUS kfile::GetName(kstringw& String)
{
	kstringw Path;
	NTSTATUS ns;
	INT Pos;
	ULONG RightCch;
	ns = GetPath(Path);
	if (!NT_SUCCESS(ns)) {

		return ns;
	}

	Pos = Path.ReverseFind(L'\\');
	if (Pos == -1) {

		return STATUS_UNSUCCESSFUL;
	}

	Pos++;

	RightCch = Path.GetLength() - Pos;

	Path.Right(RightCch);

	return ns;
}

PFILE_OBJECT kfile::GetObject()
{
	return m_fileobj;
}

NTSTATUS kfile::GetPath(kstringw& String)
{
	PUCHAR Buffer;
	kstringw Path;
	NTSTATUS ns;
	
	Buffer = new UCHAR[1024];
	RtlZeroMemory(Buffer, 1024);

	ns = GetPath((PFILE_NAME_INFORMATION)Buffer, 1024 - sizeof(WCHAR));
	if (!NT_SUCCESS(ns)) {

		delete[] Buffer;
		return ns;
	}

	String.Copy(((PFILE_NAME_INFORMATION)Buffer)->FileName, 
		((PFILE_NAME_INFORMATION)Buffer)->FileNameLength / sizeof(WCHAR));

	delete[] Buffer;
	return ns;
}

NTSTATUS kfile::GetPath( PFILE_NAME_INFORMATION NameInfo, ULONG Length)
{
	NTSTATUS ns;
	IO_STATUS_BLOCK Iob;

	return ZwQueryInformationFile(m_filehandle,
		&Iob,
		&NameInfo,
		Length,
		FileNameInformation);
}

NTSTATUS kfile::GetSize( PLARGE_INTEGER FileSize )
{
	NTSTATUS ns;
	IO_STATUS_BLOCK Iob;
	FILE_STANDARD_INFORMATION StandardInfo;

	ns = ZwQueryInformationFile(m_filehandle,
		&Iob,
		&StandardInfo,
		sizeof(StandardInfo),
		FileStandardInformation);

	if (!NT_SUCCESS(ns))
	{
		return FALSE;
	}

	FileSize->QuadPart = StandardInfo.EndOfFile.QuadPart;
	return TRUE;
}

NTSTATUS kfile::Read( PVOID Buffer, ULONG Length, PULONG RetLength /*= NULL*/ )
{
	IO_STATUS_BLOCK Iob;
	return ZwReadFile(m_filehandle, 0, NULL, NULL, &Iob, Buffer, Length, NULL, NULL);
}

NTSTATUS kfile::Rename( PFILE_RENAME_INFORMATION RenameInfo, ULONG Length )
{
	IO_STATUS_BLOCK Iob;

	return ZwSetInformationFile(m_filehandle,
		&Iob,
		RenameInfo,
		Length,
		FileRenameInformation);
}

NTSTATUS kfile::Rename( kstringw& Name, BOOLEAN ReplaceIfExists )
{
	NTSTATUS ns;
	PUCHAR Buffer;
	PFILE_RENAME_INFORMATION RenameInfo;

	Buffer = new UCHAR[1024];
	RtlZeroMemory(Buffer, 1024);
	RenameInfo = (PFILE_RENAME_INFORMATION)Buffer;

	RenameInfo->ReplaceIfExists = ReplaceIfExists;
	RenameInfo->RootDirectory = NULL;
	RenameInfo->FileNameLength = Name.GetLength() * sizeof(WCHAR);
	RtlStringCbCopyW(RenameInfo->FileName, 1024 - sizeof(FILE_RENAME_INFORMATION), Name.GetString());

	ns = Rename(RenameInfo, 1024);
	
	delete[] Buffer;

	return ns;
}

BOOLEAN kfile::SetPointer( PLARGE_INTEGER DistanceToMove, ULONG Flag /*= FILE_BEGIN*/, PLARGE_INTEGER NewPointer /*= NULL*/ )
{
	NTSTATUS Status;
	IO_STATUS_BLOCK IoStatusBlock;
	FILE_POSITION_INFORMATION CurrentPosition;
	FILE_STANDARD_INFORMATION StandardInfo;
	LARGE_INTEGER Large;

	Large.QuadPart = DistanceToMove->QuadPart;

	switch (Flag)
	{
	case FILE_BEGIN:

		CurrentPosition.CurrentByteOffset = Large;
		break;

	case FILE_CURRENT:

		Status = ZwQueryInformationFile(m_filehandle,
			&IoStatusBlock,
			&CurrentPosition,
			sizeof(CurrentPosition),
			FilePositionInformation);

		if (!NT_SUCCESS(Status))
		{
			return FALSE;
		}

		CurrentPosition.CurrentByteOffset.QuadPart += Large.QuadPart;
		break;

	case FILE_END:

		Status = ZwQueryInformationFile(m_filehandle,
			&IoStatusBlock,
			&StandardInfo,
			sizeof(StandardInfo),
			FileStandardInformation);

		if (!NT_SUCCESS(Status))
		{
			return FALSE;
		}

		CurrentPosition.CurrentByteOffset.QuadPart = StandardInfo.EndOfFile.QuadPart + Large.QuadPart;
		break;

	default:

		return FALSE;
	}

	if (CurrentPosition.CurrentByteOffset.QuadPart < 0)
	{
		return FALSE;
	}

	Status = ZwSetInformationFile(m_filehandle,
		&IoStatusBlock,
		&CurrentPosition,
		sizeof(CurrentPosition),
		FilePositionInformation);

	if (NT_SUCCESS(Status))
	{
		if (ARGUMENT_PRESENT(NewPointer))
		{
			*NewPointer = CurrentPosition.CurrentByteOffset;
		}
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

NTSTATUS kfile::SetEnd(PLARGE_INTEGER EndPos)
{
	IO_STATUS_BLOCK Iob;
	FILE_END_OF_FILE_INFORMATION FileEnd;
	
	FileEnd.EndOfFile.QuadPart = EndPos->QuadPart;

	return ZwSetInformationFile(m_filehandle,
		&Iob,
		&FileEnd,
		sizeof(FileEnd),
		FileEndOfFileInformation);
}

NTSTATUS kfile::Write( PVOID Buffer, ULONG Length, PULONG RetLength /*= NULL*/ )
{
	IO_STATUS_BLOCK Iob;
	return ZwWriteFile(m_filehandle, 0, NULL, NULL, &Iob, Buffer, Length, NULL, NULL);
}






